package com.wteam.dragon.system.util;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.util.IdUtil;
import cn.hutool.poi.excel.BigExcelWriter;
import cn.hutool.poi.excel.ExcelUtil;
import com.wteam.dragon.system.exception.BadRequestException;
import lombok.AllArgsConstructor;
import lombok.Data;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.springframework.http.HttpStatus;
import org.springframework.web.multipart.MultipartFile;

import javax.activation.MimetypesFileTypeMap;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.security.MessageDigest;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Author 陈晓辉
 * @Date 2020/4/1 16:54
 * 文件配置类
 * 支持文件：
 * String documents = "txt doc pdf ppt pps xlsx xls docx";
 * String music = "mp3 wav wma mpa ram ra aac aif m4a";
 * String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg";
 * String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg";
 */
public class FileUtil extends cn.hutool.core.io.FileUtil {
    /**
     * 定义GB的计算常量
     */
    private static final int GB = 1024 * 1024 * 1024;
    /**
     * 定义MB的计算常量
     */
    private static final int MB = 1024 * 1024;
    /**
     * 定义KB的计算常量
     */
    private static final int KB = 1024;
    /**
     * 格式化小数
     */
    private static final DecimalFormat DF = new DecimalFormat("0.00");

    /**
     * 文件类型
     */
    public enum FileType {
        //图片
        PICTURE,
        //视频
        VIDEO,
        //文档
        DOCUMENTS,
        //音乐
        MUSIC,
    }

    /**
     * @param file     上传的文件
     * @param typePath 上传文件的路径（名字）
     * @param fileType 上传文件的类型
     * @return
     * @throws FileNotFoundException
     */
    public static FilePath uploadFile(MultipartFile file, String typePath, FileType fileType) {
        String documents = "txt doc pdf ppt pps xlsx xls docx";
        String music = "mp3 wav wma mpa ram ra aac aif m4a";
        String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg";
        String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg";

        if (file.isEmpty()) {
            throw new BadRequestException("文件为空");
        }
        //获取文件扩展名
        String extensionName = getExtensionName(file.getOriginalFilename());
        if (extensionName == null) {
            throw new BadRequestException(HttpStatus.NOT_IMPLEMENTED, "展不支持该文件类型");
        }
        switch (fileType) {
            case PICTURE: {
                if (!image.contains(extensionName)) {
                    throw new BadRequestException(HttpStatus.NOT_IMPLEMENTED, "展不支持该文件类型");
                }
                break;
            }
            case VIDEO: {
                if (!video.contains(extensionName)) {
                    throw new BadRequestException(HttpStatus.NOT_IMPLEMENTED, "展不支持该文件类型");
                }
                break;
            }
            case MUSIC: {
                if (!music.contains(extensionName)) {
                    throw new BadRequestException(HttpStatus.NOT_IMPLEMENTED, "展不支持该文件类型");
                }
                break;
            }
            case DOCUMENTS: {
                if (!documents.contains(extensionName)) {
                    throw new BadRequestException(HttpStatus.NOT_IMPLEMENTED, "展不支持该文件类型");
                }
                break;
            }
            default: {
                throw new BadRequestException(HttpStatus.NOT_IMPLEMENTED, "展不支持该文件类型");
            }
        }
        FilePath filePath = getPath(file, typePath);
        File folder = new File(filePath.getFullPath());
        if (!folder.getParentFile().exists()) {
            folder.getParentFile().mkdirs();
        }
        try {
            //进行文件的处理（暂时只有图片的处理）
            switch (fileType) {
                //进行图片压缩处理
                case PICTURE: {
                    Thumbnails.of(file.getInputStream())
                            .scale(1f)
                            .outputQuality(0.3f)
                            .toFile(folder.getAbsolutePath());
                    break;
                }
                default: {
                    file.transferTo(folder);
                }
            }
            return filePath;
        } catch (IOException e) {
            e.printStackTrace();
            throw new BadRequestException("上传失败");
        }
    }

    /**
     * @param uploadFile 上传的文件
     * @param typePath   上传文件的包名（例如ArticlePicture）
     */
    private static FilePath getPath(MultipartFile uploadFile, String typePath) {
        //获取文件名
        String fileName = uploadFile.getOriginalFilename();
        //获取文件后缀
        assert fileName != null;
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        //静态文件夹名称
        String staticPath = "file";
        //路径夹
        String filepath = getJarRootPath() + File.separator + staticPath;
        //时间
        SimpleDateFormat time = new SimpleDateFormat("yyyyMMdd");
        String format = time.format(new Date());
        //对文件的名字进行uuid处理保证名字的唯一性
        UUID uuid = UUID.randomUUID();
        fileName = uuid.toString() + suffixName;
        //上传文件总路径
        String path = filepath + File.separator + format + File.separator + typePath + File.separator + fileName;
        //发送给前端的路径
        String uploadFilePath = "";
        //获得系统名
        String os = System.getProperty("os.name");
        if (os.toLowerCase().startsWith(ConstantUtil.WIN)) {
            uploadFilePath = File.separator + staticPath + File.separator + format + File.separator + typePath + File.separator + fileName;
        } else {
            uploadFilePath = path;
        }
        return new FilePath(path, uploadFilePath);
    }

    /**
     * 获得储存的文件路径
     *
     * @param typePath 储存的文件路径（在已经设置好的路径后面拼接 /设置好的路径/typePath）
     */
    public static FilePath getPath(FileType fileType, String typePath) throws FileNotFoundException {
        //获取文件后缀
        String suffixName = "";
        if (fileType.equals(FileType.PICTURE)) {
            suffixName = ".png";
        } else if (fileType.equals(FileType.VIDEO)) {
            suffixName = ".mp4";
        }
        //静态文件夹名称
        String staticPath = "upload";
        //路径夹
        String filepath = getJarRootPath() + File.separator + staticPath;
        //时间
        SimpleDateFormat time = new SimpleDateFormat("yyyyMMdd");
        String format = time.format(new Date());
        //对文件的名字进行uuid处理保证名字的唯一性
        UUID uuid = UUID.randomUUID();
        String fileName = uuid.toString() + suffixName;
        //上传文件总路径
        File file = new File(filepath + File.separator + format + File.separator + typePath);
        if (!file.exists()) {
            file.mkdirs();
        }
        String path = filepath + File.separator + format + File.separator + typePath + File.separator + fileName;
        //发送给前端的路径
        String uploadFilePath = "";
        //获得系统名
        String os = System.getProperty("os.name");
        if (os.toLowerCase().startsWith(ConstantUtil.WIN)) {
            uploadFilePath = staticPath + File.separator + format + File.separator + typePath + File.separator + fileName;
        } else {
            uploadFilePath = path;
        }
        return new FilePath(path, uploadFilePath);
    }

    /**
     * 获得静态文件的路径 classpath
     *
     * @return
     * @throws FileNotFoundException
     */
    protected static String getJarRootPath() {
        ClassPathResource classPathResource = new ClassPathResource("");
        String path = classPathResource.getAbsolutePath();

        File rootFile = new File(path);
        if (!rootFile.exists()) {
            rootFile = new File("");
        }
        return rootFile.getAbsolutePath();
    }

    /**
     * MultipartFile类型转File类型
     *
     * @param multipartFile
     * @return
     */
    public static File multipartFileToFile(MultipartFile multipartFile) {
        // 获取文件名
        String fileName = multipartFile.getOriginalFilename();
        // 获取文件后缀
        String prefix = "." + getExtensionName(fileName);
        File file = null;
        try {
            // 用uuid作为文件名，防止生成的临时文件重复
            file = File.createTempFile(IdUtil.simpleUUID(), prefix);
            // MultipartFile to File
            multipartFile.transferTo(file);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }


    /**
     * 删除文件
     *
     * @param files 多个File
     */
    public static void deleteFile(File... files) {
        for (File file : files) {
            if (file.exists()) {
                file.delete();
            }
        }
    }

    /**
     * 获取文件的扩展名 不带.
     *
     * @param filename 文件名 或者 文件路径
     * @return
     */
    public static String getExtensionName(String filename) {
        if ((filename != null) && (filename.length() > 0)) {
            int dot = filename.lastIndexOf('.');
            if ((dot > -1) && (dot < (filename.length() - 1))) {
                return filename.substring(dot + 1);
            }
        }
        return filename;
    }

    /**
     * Java文件操作 获取不带扩展名的文件名
     *
     * @param filename 文件名 或者 文件路径
     * @return
     */
    public static String getFileNameNoEx(String filename) {
        if ((filename != null) && (filename.length() > 0)) {
            int dot = filename.lastIndexOf('.');
            if ((dot > -1) && (dot < (filename.length()))) {
                return filename.substring(0, dot);
            }
        }
        return filename;
    }

    /**
     * 获得文件的大小
     *
     * @param size 字节数
     * @return
     */
    public static String getSize(long size) {
        String resultSize;
        if (size / GB >= 1) {
            //如果当前Byte的值大于等于1GB
            resultSize = DF.format(size / (float) GB) + "GB   ";
        } else if (size / MB >= 1) {
            //如果当前Byte的值大于等于1MB
            resultSize = DF.format(size / (float) MB) + "MB   ";
        } else if (size / KB >= 1) {
            //如果当前Byte的值大于等于1KB
            resultSize = DF.format(size / (float) KB) + "KB   ";
        } else {
            resultSize = size + "B   ";
        }
        return resultSize;
    }


    /**
     * 将转化为文件
     *
     * @param ins  输入流
     * @param name 文件名字
     * @return
     * @throws Exception
     */
    static File inputStreamToFile(InputStream ins, String name) throws Exception {
        File file = new File(System.getProperty("java.io.tmpdir") + File.separator + name);
        if (file.exists()) {
            return file;
        }
        OutputStream os = new FileOutputStream(file);
        int bytesRead;
        int len = 8192;
        byte[] buffer = new byte[len];
        while ((bytesRead = ins.read(buffer, 0, len)) != -1) {
            os.write(buffer, 0, bytesRead);
        }
        os.close();
        ins.close();
        return file;
    }

    /**
     * 将文件转化为Base64编码格式
     *
     * @param file 文件
     * @return
     * @throws Exception
     */
    public static String fileToBase64(File file) throws Exception {
        FileInputStream inputFile = new FileInputStream(file);
        String base64;
        byte[] buffer = new byte[(int) file.length()];
        inputFile.read(buffer);
        inputFile.close();
        base64 = Base64.encode(buffer);
        return base64.replaceAll("[\\s*\t\n\r]", "");
    }


    /**
     * 下载Excel文件
     *
     * @param list
     * @param response
     * @throws IOException
     */
    public static void downloadExcel(List<Map<String, Object>> list, HttpServletResponse response) throws IOException {
        String tempPath = System.getProperty("java.io.tmpdir") + IdUtil.fastSimpleUUID() + ".xlsx";
        File file = new File(tempPath);
        BigExcelWriter writer = ExcelUtil.getBigWriter(file);
        if (list.isEmpty()) {
            response.setStatus(400);
            throw new BadRequestException("选择的区间为空");
        }
        // 一次性写出内容，使用默认样式，强制输出标题
        writer.write(list, true);
        SXSSFSheet sheet = (SXSSFSheet) writer.getSheet();
        //上面需要强转SXSSFSheet  不然没有trackAllColumnsForAutoSizing方法
        sheet.trackAllColumnsForAutoSizing();
        //列宽自适应
        writer.autoSizeColumnAll();
        //response为HttpServletResponse对象
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        //test.xls是弹出下载对话框的文件名，不能为中文，中文请自行编码
        response.setHeader("Content-Disposition", "attachment;filename=file.xlsx");
        ServletOutputStream out = response.getOutputStream();
        // 终止后删除临时文件
        file.deleteOnExit();
        writer.flush(out, true);
        //此处记得关闭输出Servlet流
        IoUtil.close(out);
    }

    /**
     * 获得文件的类型
     *
     * @param type 文件的后缀名（不带点）
     * @return
     */
    public static String getFileType(String type) {
        String documents = "txt doc pdf ppt pps xlsx xls docx";
        String music = "mp3 wav wma mpa ram ra aac aif m4a";
        String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg";
        String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg";
        if (image.contains(type)) {
            return "图片";
        } else if (documents.contains(type)) {
            return "文档";
        } else if (music.contains(type)) {
            return "音乐";
        } else if (video.contains(type)) {
            return "视频";
        } else {
            return "其他";
        }
    }

    /**
     * 获得Mime类型
     *
     * @param type 文件类型
     * @return
     */
    public static String getFileTypeByMimeType(String type) {
        String mimeType = new MimetypesFileTypeMap().getContentType("." + type);
        return mimeType.split("/")[0];
    }

    /**
     * 检查文件是否超出限定值
     *
     * @param maxSize 最大值（单位为M）
     * @param size    字节数
     */
    public static void checkSize(long maxSize, long size) {
        // 1M
        int len = 1024 * 1024;
        if (size > (maxSize * len)) {
            throw new BadRequestException("文件超出规定大小");
        }
    }

    /**
     * 判断两个文件是否相同
     *
     * @param file1 文件一
     * @param file2 文件二
     * @return
     */
    public static boolean check(File file1, File file2) {
        String img1Md5 = getMd5(file1);
        String img2Md5 = getMd5(file2);
        return img1Md5.equals(img2Md5);
    }

    /**
     * 判断两个文件是否相同
     *
     * @param file1Md5
     * @param file2Md5
     * @return
     */
    public static boolean check(String file1Md5, String file2Md5) {
        return file1Md5.equals(file2Md5);
    }

    /**
     * 将文件转化为字节数组
     *
     * @param file
     * @return
     */
    private static byte[] getByte(File file) {
        // 得到文件长度
        byte[] b = new byte[(int) file.length()];
        try {
            InputStream in = new FileInputStream(file);
            try {
                in.read(b);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        }
        return b;
    }

    /**
     * 通过字节数组获得Md5
     *
     * @param bytes 字节数组
     * @return
     */
    private static String getMd5(byte[] bytes) {
        // 16进制字符
        char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        try {
            MessageDigest mdTemp = MessageDigest.getInstance("MD5");
            mdTemp.update(bytes);
            byte[] md = mdTemp.digest();
            int j = md.length;
            char[] str = new char[j * 2];
            int k = 0;
            // 移位 输出字符串
            for (byte byte0 : md) {
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(str);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 下载文件
     *
     * @param request
     * @param response
     * @param file         文件File
     * @param deleteOnExit 是否将源文件删除
     */
    public static void downloadFile(HttpServletRequest request, HttpServletResponse response, File file, boolean deleteOnExit) {
        response.setCharacterEncoding(request.getCharacterEncoding());
        response.setContentType("application/octet-stream");
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            response.setHeader("Content-Disposition", "attachment; filename=" + file.getName());
            IOUtils.copy(fis, response.getOutputStream());
            response.flushBuffer();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                    if (deleteOnExit) {
                        file.deleteOnExit();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 将文件转化为MD5
     *
     * @param file 文件File
     * @return
     */
    public static String getMd5(File file) {
        return getMd5(getByte(file));
    }

    @Data
    @AllArgsConstructor
    public static class FilePath {
        //全路径
        private String fullPath;

        //访问路径
        private String visitPath;
    }
}











